package biolean.logic;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;

import javax.imageio.ImageIO;

import biolean.Commons;
import biolean.Utils;

import math.geom2d.Point2D;
import math.geom2d.Vector2D;
import math.geom2d.conic.Circle2D;
import math.geom2d.line.LineSegment2D;

public class Connection implements Commons {
	public GraphicElement outputElem;
	public GraphicElement inputElem;
	private boolean active = false;
	// body stredov elementov
	private Point2D xx;
	private Point2D yy;
	// body priesecnikov
	int outX, outY, inpX, inpY;
	// body rectangle ak je file
	private Point2D a;
	private Point2D b;
	private Point2D c;
	private Point2D d;
	// obrazok sipky
	private BufferedImage bufferedImage;

	/**
	 * @author Martin Hanisko
	 */

	public Connection(GraphicElement elem1, GraphicElement elem2) {
		outputElem = elem1;
		inputElem = elem2;
		setPoints();
	}
	
	// nastavi sa sipka na active > vykresli sa modrou
	public void setActive(boolean active) {
		this.active = active;
	}
	
	public boolean getActive() {
		return active;
	}

	// inicializacia suradnic obldznika
	private void setRectangle(GraphicElement elem) {
		int halfWidth = elem.width / 2;
		int halfHeigh = FILE_HEIGHT / 2;
		a = new Point2D(elem.x - halfWidth, elem.y + halfHeigh);
		b = new Point2D(elem.x + halfWidth, elem.y + halfHeigh);
		c = new Point2D(elem.x + halfWidth, elem.y - halfHeigh);
		d = new Point2D(elem.x - halfWidth, elem.y - halfHeigh);
	}

	private void setPoints() {
		xx = new Point2D(outputElem.x, outputElem.y);
		yy = new Point2D(inputElem.x, inputElem.y);
	}

	// priesecniky pri spojeni command-command NEPOUZIVA SA
	public void setCommandCommandIntersection() {
		Point2D x = null;
		Point2D y = null;
		int radius = COMMAND_HEIGHT / 2;
		Circle2D outCmd = new Circle2D(outputElem.x, outputElem.y, radius);
		Circle2D inpCmd = new Circle2D(inputElem.x, inputElem.y, radius);
		LineSegment2D line = new LineSegment2D(outputElem.x, outputElem.y,
				inputElem.x, inputElem.y);
		x = intersectonCirlceLine(outCmd, line);
		y = intersectonCirlceLine(inpCmd, line);
		setIntersectionPoints(x, y);
	}
	
	// skontroluje prekritie commandov NEPOUZIVA SA
	public boolean checkCommandDistance() {
		if (xx.distance(yy) <= COMMAND_WIDTH) {
			return false;
		}
		return true;
	}

	// priesenicky pri spojeni command-file
	private void setCommandFileIntersection() {
		Point2D x = null;
		Point2D y = null;
		int radius = COMMAND_HEIGHT / 2;
		LineSegment2D line = new LineSegment2D(outputElem.x, outputElem.y,
				inputElem.x, inputElem.y);
		if (Utils.isCommand( outputElem )) {
			Circle2D outCmd = new Circle2D(outputElem.x, outputElem.y, radius);
			setRectangle(inputElem);
			x = intersectonCirlceLine(outCmd, line);
			y = intersectionRectangleLine(a, b, c, d, xx, yy);
			setIntersectionPoints(x, y);
		} else {
			Circle2D inpCmd = new Circle2D(inputElem.x, inputElem.y, radius);
			setRectangle(outputElem);
			x = intersectonCirlceLine(inpCmd, line);
			y = intersectionRectangleLine(a, b, c, d, xx, yy);
			setIntersectionPoints(y, x);
		}

	}

	// priesecnik kruhu a priamky
	private Point2D intersectonCirlceLine(Circle2D circle, LineSegment2D line) {
		Point2D result = null;
		Collection<Point2D> intersect = circle.intersections(line);
		Iterator<Point2D> itr = intersect.iterator();
		if (itr.hasNext()) {
			result = (Point2D) itr.next();
		}
		return result;
	}

	// priesenick obdlznika a priamky
	private Point2D intersectionRectangleLine(Point2D a, Point2D b, Point2D c,
			Point2D d, Point2D x, Point2D y) {
		Point2D result = null;
		LineSegment2D testLine, line;
		line = new LineSegment2D(x, y);
		testLine = new LineSegment2D(a, b);
		result = line.intersection(testLine);
		if (result == null) {
			testLine = new LineSegment2D(b, c);
			result = line.intersection(testLine);
		}
		if (result == null) {
			testLine = new LineSegment2D(c, d);
			result = line.intersection(testLine);
		}
		if (result == null) {
			testLine = new LineSegment2D(d, a);
			result = line.intersection(testLine);
		}
		return result;
	}

	// priesecnik stovrca a kruhu
	private Point2D intersectionRectangleCircle(Point2D a, Point2D b, Point2D c,
			Point2D d, Circle2D circle) {
		Point2D result = null;
		LineSegment2D testLine = new LineSegment2D(a, b);
		Collection<Point2D> intersect = circle.intersections(testLine);
		Iterator<Point2D> itr = intersect.iterator();
		if (!intersect.isEmpty()) {
			return (Point2D) itr.next();
		}
		testLine = new LineSegment2D(b, c);
		intersect = circle.intersections(testLine);
		itr = intersect.iterator();
		if (!intersect.isEmpty()) {
			return (Point2D) itr.next();
		}
		testLine = new LineSegment2D(c, d);
		intersect = circle.intersections(testLine);
		itr = intersect.iterator();
		if (!intersect.isEmpty()) {
			return (Point2D) itr.next();
		}
		testLine = new LineSegment2D(d, a);
		intersect = circle.intersections(testLine);
		itr = intersect.iterator();
		if (!intersect.isEmpty()) {
			return (Point2D) itr.next();
		}
		return result;
	}

	private void setIntersectionPoints(Point2D a, Point2D b) {
		if ((a != null) && (b != null)) {
			outX = a.getAsInt().x;
			outY = a.getAsInt().y;
			inpX = b.getAsInt().x;
			inpY = b.getAsInt().y;
		}
	}

	// skontroluje prekritie file-command
	private boolean checkCommandFileIntersection() {
		if (xx.distance(yy) <= (COMMAND_WIDTH / 2)) {
			return false;
		} else {
			Point2D x = null;
			int radius = COMMAND_HEIGHT / 2;
			if (Utils.isCommand( outputElem )) {
				Circle2D outCmd = new Circle2D(outputElem.x, outputElem.y,
						radius);
				setRectangle(inputElem);
				x = intersectionRectangleCircle(a, b, c, d, outCmd);
			} else {
				Circle2D inpCmd = new Circle2D(inputElem.x, inputElem.y, radius);
				setRectangle(outputElem);
				x = intersectionRectangleCircle(a, b, c, d, inpCmd);
			}
			if (x != null) {
				return false;
			}
		}
		return true;
	}

	// vykreslenie spojenia (sipky)
	private void drawConnection(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		g2.setStroke(CONNECTION_LINE_WIDTH);
		if (active) {
			g2.setColor(Color.BLUE);
			loadImage(ACTIVE_ARROW);
		} else {
			loadImage(INACTIVE_ARROW);
		}
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g2.drawLine(outX, outY, inpX, inpY);
		rotateImage();
		g2.drawImage(bufferedImage, inpX - 10, inpY - 10, null);
	}

	// rotacia obrazku
	private void rotateImage() {
		AffineTransform tx = new AffineTransform();
		Vector2D xy = new Vector2D(xx, yy); // vektor priamky
		double angle = xy.angle();
		tx.rotate(Math.PI / 2 + angle, bufferedImage.getWidth() / 2,
				bufferedImage.getHeight() / 2);
		AffineTransformOp op = new AffineTransformOp(tx,
				AffineTransformOp.TYPE_BILINEAR);
		bufferedImage = op.filter(bufferedImage, null);
	}

	// nacitanie obrazku
	private void loadImage(String path) {
		try {
			bufferedImage = ImageIO.read(new File(path));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// zistime ci sme nad connection (tolerancia MOUSE_CONNECTION_DIST)
	public boolean isMouseOverConnection(int mouseX, int mouseY, Connection conn) {
		Circle2D mouseCirle = new Circle2D(mouseX, mouseY,
				MOUSE_CONNECTION_DIST);
		LineSegment2D connLine = new LineSegment2D(conn.xx, conn.yy);
		Point2D p = intersectonCirlceLine(mouseCirle, connLine);
		if (p != null) {
			return true;
		}
		return false;
	}

	public void draw(Graphics g) {
		g.setColor(Color.BLACK);
		setPoints();
		if (Utils.isCommand( outputElem ) && Utils.isBioFile(inputElem )) {
			setCommandFileIntersection();
			if (checkCommandFileIntersection()) {
				drawConnection(g);
			}
		} else if (Utils.isBioFile(outputElem) && Utils.isCommand(inputElem)) {
			setCommandFileIntersection();
			if (checkCommandFileIntersection()) {
				drawConnection(g);
			}
		}
	}

}
